#include "os/os.h"
#include "os_util.h"
#include "portable.h"
#include <string.h>

#if defined(CONFIG_OS_HEAP_TRACE) && (CONFIG_OS_HEAP_TRACE > 0)

#include "list/slist.h"

#undef SYS_LOG_DOMAIN
#define SYS_LOG_DOMAIN "HEAP"
#include "sys_log.h"

static slist_t s_list;

struct __s_node
{
    slist_node_t node;
    const char *file;
    uint32_t line; // 新增行号字段
    uint32_t size;
    uint32_t time;
};

/**
 * @brief 把内存插入到链表中
 *
 * @param memory
 * @return void* 插入链表后可用的内存指针
 */
static void *_list_insert(struct __s_node *memory, const char *file, uint32_t line, uint32_t size)
{
    slist_init_node(&memory->node);
    slist_insert_front(&s_list, &memory->node);
    memory->file = file;
    memory->line = line; // 设置行号
    memory->size = size;
    memory->time = os_get_sys_time();
    return &memory[1];
}

/**
 * @brief 从链表中移除内存
 *
 * @param data
 * @return void* 移除链表后原始的内存指针
 */
static void *_list_remove(void *data)
{
    struct __s_node *memory = &((struct __s_node *)data)[-1];
    slist_remove(&s_list, &memory->node);
    return memory;
}

/**
 * @brief 打印已通过 os_heap_trace_malloc 申请的堆内存信息
 *
 * @param after_time 系统时间大于此时间的被打印。单位 毫秒
 */
void os_heap_list_trace(uint32_t after_time)
{
    SYS_PRINT("      time     size        addr    file:line\r\n");
    SYS_PRINT("------------------------------------------------\r\n");
    struct __s_node *memory;
    SLIST_FOR_EACH_CONTAINER(&s_list, memory, node)
    {
        if (memory->time > after_time)
        {
            SYS_PRINT("%10.3f %8u  0x%08x\t%s:%u\r\n",
                      (float)memory->time / 1000, memory->size,
                      (uint32_t)&memory[1], memory->file, memory->line);
        }
    }
}

void *os_heap_trace_malloc(size_t size, const char *file, uint32_t line)
{
    struct __s_node *memory = (struct __s_node *)pvPortMalloc(sizeof(struct __s_node) + size);
    if (memory == NULL)
    {
        return NULL;
    }
    return _list_insert(memory, file, line, size);
}

void *os_heap_trace_calloc(size_t size, const char *file, uint32_t line)
{
    struct __s_node *memory = (struct __s_node *)pvPortMalloc(sizeof(struct __s_node) + size);
    if (memory == NULL)
    {
        return NULL;
    }
    memset(&memory[1], 0, size);
    return _list_insert(memory, file, line, size);
}

void *os_heap_trace_realloc(void *p, size_t size, const char *file, uint32_t line)
{
    if (p == NULL)
    {
        return os_heap_trace_malloc(size, file, line);
    }

    struct __s_node *old_memory = &((struct __s_node *)p)[-1];
    struct __s_node *new_memory = (struct __s_node *)pvPortMalloc(sizeof(struct __s_node) + size);
    if (new_memory == NULL)
    {
        return NULL;
    }

    size_t copy_size = old_memory->size > size ? size : old_memory->size;
    memcpy(&new_memory[1], p, copy_size);
    _list_remove(p);

    return _list_insert(new_memory, file, line, size);
}

void os_heap_trace_free(void *p, const char *file)
{
    if (p == NULL)
    {
        return;
    }

    struct __s_node *memory = &((struct __s_node *)p)[-1];
    _list_remove(p);
    vPortFree(memory);
}

#else

void *os_malloc(size_t size)
{
    return pvPortMalloc(size);
}

void *os_calloc(size_t size)
{
    void *ret = os_malloc(size);
    if (ret)
    {
        memset(ret, 0, size);
    }
    return ret;
}

void *os_realloc(void *p, size_t size)
{
#if (configSTACK_ALLOCATION_FROM_SEPARATE_HEAP == 1)
    return realloc(p, size);
#else
    OS_DBG("%s() fail @ %d function not support!\r\n", __func__, __LINE__);
    return NULL;
#endif
}

void os_free(void *p)
{
    vPortFree(p);
}

void os_heap_info(size_t *used_size, size_t *free_size, size_t *max_block_size)
{
    if (max_block_size)
    {
        HeapStats_t stats;
        vPortGetHeapStats(&stats);
        *max_block_size = stats.xSizeOfLargestFreeBlockInBytes;
    }
    if (used_size)
        *used_size = 0;
    if (free_size)
        *free_size = xPortGetFreeHeapSize();
}

void os_heap_list_trace(uint32_t time)
{
    OS_WRN(" %s() fail @ %d function not support!\r\n", __func__, __LINE__);
}

#endif
